package impl

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"gitee.com/go-course/go8/projects/devcloud/mcenter/apps/token"
	"gitee.com/go-course/go8/projects/devcloud/mcenter/conf"
	"github.com/go-redis/redis/v8"
)

func cacheWrapper(impl *service) *cachedservice {
	return &cachedservice{
		service: impl,
		expired: 60 * 60 * 24 * time.Second,
	}
}

// 缓存装饰器, 增强原来的service对象
type cachedservice struct {
	*service
	redis   *redis.Client
	expired time.Duration
}

func (s *cachedservice) Config() error {
	if err := s.service.Config(); err != nil {
		return err
	}

	s.redis = conf.C().App.Redis.Client()
	return nil
}

// 添加缓存
func (c *cachedservice) ValidateToken(ctx context.Context, req *token.ValidateTokenRequest) (
	*token.Token, error) {

	// 查看token是否在redis中存在
	// tk, err := GetTokenFromRedis()
	jsonStr := ""
	if err := c.redis.Get(ctx, req.AccessToken).Scan(&jsonStr); err != nil {
		c.log.Debugf("get access token from redis error, %s", req.AccessToken)
	} else {
		tk := &token.Token{}
		err = json.Unmarshal([]byte(jsonStr), tk)
		if err != nil {
			c.log.Warnf("unmarshal error, %s", err)
			goto GetFromDB
		}
		// 如果没报错, 说明redis有
		if tk.IsAccessTokenExpired() {
			return nil, fmt.Errorf("token has expired")
		}
		c.log.Debugf("get token %s from cache", tk.AccessToken)
		return tk, nil
	}

GetFromDB:
	// 原来功能, 从数据库中
	tk, err := c.service.ValidateToken(ctx, req)
	if err != nil {
		return nil, err
	}
	// 返回之前设置缓存
	if err := c.redis.Set(ctx, tk.AccessToken, tk.ToJson(), c.expired).Err(); err != nil {
		c.log.Warnf("set token to cache error, %s", tk.AccessToken)
	} else {
		c.log.Debugf("set token %s to cache sucess", tk.AccessToken)
	}

	return tk, nil
}

// 清理缓存
func (c *cachedservice) RevolkToken(ctx context.Context, req *token.RevolkTokenRequest) (
	*token.Token, error) {
	// 原来功能
	tk, err := c.service.RevolkToken(ctx, req)
	if err != nil {
		return nil, err
	}

	// 清理缓存
	err = c.redis.Del(ctx, req.AccessToken).Err()
	if err != nil {
		c.log.Warnf("delete token %s cache error, %s", req.AccessToken)
	}

	return tk, nil
}
